using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using Fuse8.ContentManagementFramework.DataAccess.SqlAccess;
using Fuse8.ContentManagementFramework.Domain;
using Fuse8.DomainFramework.Common.Locator;
using Fuse8.DomainFramework.DataAccess;
using Fuse8.DomainFramework.DataAccess.SqlDatabase;

namespace Fuse8.ContentManagementFramework.BizFacade.Services.Contents.Queryable
{
    internal class CmsContentQueryProvider : IQueryProvider
    {
        public int ContentTypeId { get; set; }
        public bool IgnoreRevisionsSlicer { get; set; }

        public CmsContentQueryProvider(int id, bool ignoreRevisionsSlicer = false)
        {
            ContentTypeId = id;
            IgnoreRevisionsSlicer = ignoreRevisionsSlicer;
        }

        public IQueryable CreateQuery(Expression expression)
        {
            return new CmsContentQueryable(ContentTypeId, expression, IgnoreRevisionsSlicer);
        }

        public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
        {
            return new CmsContentQueryable(ContentTypeId, expression, IgnoreRevisionsSlicer) as IQueryable<TElement>;
        }

        public object Execute(Expression expression)
        {
            throw new NotImplementedException("Call generic method");
        }

        public TResult Execute<TResult>(Expression expression)
        {
            var builder = new CmsContentQueryBuilder(ContentTypeId, IgnoreRevisionsSlicer);

            bool nonQuery = PrepareBuilder(expression, builder);

            string query = builder.BuildQuery();

            var factory = DependencyServiceLocator.Current.GetInstance<IUnitOfWorkFactory<GenericCmsEntity>>();

            var currentUnitOfWork = factory.Get(false) as SqlUnitOfWork<GenericCmsEntity>;

            IList<SqlParameter> parameters = new List<SqlParameter>();
            int i = 0;
            foreach (var parameter in builder.Parameters)
            {
                var sqlParameter = new SqlParameter("p" + i, parameter);
                if (parameter == null)
                {
                    sqlParameter.Value = DBNull.Value;
                }
                
                if (parameter is string)
                {
                    var length = parameter.ToString().Length;
                    if (length > 4000)
                    {
                        sqlParameter.Size = -1;
                    }
                    else
                    {
                        sqlParameter.Size = length;
                    }
                }
                parameters.Add(sqlParameter);
                i++;
            }
            var mapper = DependencyServiceLocator.Current.GetInstance<ISqlEntityDataMapper<GenericCmsEntity, int>>();

            if (!nonQuery)
            {                
                return
                    (TResult)
                    SqlUtility<GenericCmsEntity, int>.ExecuteReader(currentUnitOfWork.Connection,
                                                                    currentUnitOfWork.Transaction, query, parameters,
                                                                    mapper, CommandType.Text);
            }
            else
            {
                return (TResult)((object)SqlUtility<GenericCmsEntity, int>.ExecuteNonQuery(currentUnitOfWork.Connection,
                                                                         currentUnitOfWork.Transaction, query,
                                                                         parameters, mapper, CommandType.Text, true));
            }
        }

        internal static bool PrepareBuilder(Expression expression, CmsContentQueryBuilder builder)
        {
            bool nonQuery = false;
            var validator = new ContentQueryExpressionValidator();
            validator.Parse(expression);
            foreach (var pair in validator.Expressions)
            {
                var operation = SupportedQueryableOperationsFacade.Operations[pair.Key];
                if (operation.BuidAction != null)
                {
                    operation.BuidAction(builder, pair.Value);
                }
                if (operation.IsNonQuery)
                {
                    nonQuery = true;
                }
            }
            return nonQuery;
        }
    }
}